package au.org.ala.phylojive

import org.springframework.dao.DataIntegrityViolationException
import javax.annotation.PostConstruct
import grails.converters.JSON

class TreeController {

    def userService
    def authService
    def charService

    static allowedMethods = [save: "POST", update: "POST", delete: "POST"]

    def ADMIN_ROLE

    @PostConstruct
    def init() {
        ADMIN_ROLE = grailsApplication.config.auth.admin_role
    }

    def index() {
        redirect(action: "list", params: params)
    }

    def list() {
        log.info "userId = " + authService.getUserId()
        params.max = Math.min(params.max ? params.int('max') : 10, 100)

        [treeInstanceList: Tree.list(params), treeInstanceTotal: Tree.count()]
    }

    def create() {
        userService.registerCurrentUser()
        log.debug "userId = " + authService.getUserId()
        def currentUser = User.findByUserId(authService.getUserId()?:-1)

        if (currentUser) {
            params.owner = currentUser
            [treeInstance: new Tree(params)]
        } else {
            def msg = "Failed to get details for current logged-in user"
            log.error msg
            flash.message = message(msg, args: [message(code: 'tree.label', default: 'Tree'), params.id])
            redirect(action: "list")
        }
    }

    def save() {
        def treeInstance = new Tree(params)
        if (!treeInstance.save(flush: true)) {
            render(view: "create", model: [treeInstance: treeInstance])
            return
        }

		flash.message = message(code: 'default.created.message', args: [message(code: 'tree.label', default: 'Tree'), treeInstance.id])
        redirect(action: "show", id: treeInstance.id)
    }

    def show() {
        def treeInstance = Tree.get(params.id)

        if (!treeInstance) {
			flash.message = message(code: 'default.not.found.message', args: [message(code: 'tree.label', default: 'Tree'), params.id])
            redirect(action: "list")
            return
        }

        if (treeInstance.isPublic || checkPermissions(treeInstance.owner.email)) {
            [treeInstance: treeInstance]
        }
    }

    def edit() {
        userService.registerCurrentUser()
        def currentUser = User.findByUserId(authService.getUserId()?:-1)
        def treeInstance = Tree.get(params.id)

        if (!treeInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'tree.label', default: 'Tree'), params.id])
            redirect(action: "list")
            return
        } else if (!currentUser  || !(authService.userInRole(ADMIN_ROLE) || currentUser == treeInstance.owner)) {
            flash.message = 'User not known or does not have permission (' + ADMIN_ROLE + ') to edit tree - ' + currentUser?:""
            redirect(action: "list")
            return
        }

        [treeInstance: treeInstance]
    }

    def update() {
        def currentUser = User.findByUserId(authService.getUserId()?:-1)
        def treeInstance = Tree.get(params.id)
        if (!treeInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'tree.label', default: 'Tree'), params.id])
            redirect(action: "list")
            return
        } else if (!currentUser  || !(authService.userInRole(ADMIN_ROLE) || currentUser == treeInstance.owner)) {
            flash.message = 'User not known or does not have permission (' + ADMIN_ROLE + ') to edit tree - ' + currentUser?:""
            redirect(action: "list")
            return
        }

        if (params.version) {
            def version = params.version.toLong()
            if (treeInstance.version > version) {
                treeInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
                          [message(code: 'tree.label', default: 'Tree')] as Object[],
                          "Another user has updated this Tree while you were editing")
                render(view: "edit", model: [treeInstance: treeInstance])
                return
            }
        }

        treeInstance.properties = params

        if (!treeInstance.save(flush: true)) {
            render(view: "edit", model: [treeInstance: treeInstance])
            return
        }

		flash.message = message(code: 'default.updated.message', args: [message(code: 'tree.label', default: 'Tree'), treeInstance.id])
        redirect(action: "show", id: treeInstance.id)
    }

    def delete() {
        userService.registerCurrentUser()
        // check only admin or owner can delete tree
        def currentUser = User.findByUserId(authService.getUserId()?:-1)
        def treeInstance = Tree.get(params.id)
        if (!treeInstance) {
			flash.message = message(code: 'default.not.found.message', args: [message(code: 'tree.label', default: 'Tree'), params.id])
            redirect(action: "list")
            return
        } else if (!currentUser  || !(authService.userInRole(ADMIN_ROLE) || currentUser == treeInstance.owner)) {
            flash.message = 'User not known or does not have permission (' + ADMIN_ROLE + ') to edit tree - ' + currentUser?:""
            redirect(action: "list")
            return
        }

        try {
            treeInstance.delete(flush: true)
			flash.message = message(code: 'default.deleted.message', args: [message(code: 'tree.label', default: 'Tree'), params.id])
            redirect(action: "list")
        }
        catch (DataIntegrityViolationException e) {
			flash.message = message(code: 'default.not.deleted.message', args: [message(code: 'tree.label', default: 'Tree'), params.id])
            redirect(action: "show", id: params.id)
        }
    }

    def convertCsvToJson() {
        log.debug "params = " + params
        def charMap = charService.convertCharCsvToJson(params.csv , params.separator)
        render charMap as JSON;
    }

    def generateNewPrivateUrlPath() {
        def tree = Tree.get(params.id)
        def currentUser = User.findByUserId(authService.getUserId()?:-1)
        if (!tree) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'tree.label', default: 'Tree'), params.id])
            redirect(action: "list")
            return
        } else if (!currentUser  || !(authService.userInRole(ADMIN_ROLE) || currentUser == tree.owner)) {
            flash.message = 'User not known or does not have permission (' + ADMIN_ROLE + ') to edit tree - ' + currentUser?:""
            redirect(action: "list")
            return
        }

        def newPath = encodePrivateUrlPath(tree.name + new Date().getTime())
        tree.privateUrlPath = newPath
        tree.save()
        if (!tree.save(flush: true)) {
            flash.message = "Failed to update tree with new private URL path: " + tree.errors
        }
        redirect(action: "show", id: params.id)
    }

    def encodePrivateUrlPath(String newPath) {
        log.debug "new path = " + newPath
        newPath.bytes.encodeBase64().toString()
    }

    def convertCharJsonToCsv() {
        def treeInstance = Tree.get(params.id)
        log.debug("checking params" + params)
        if (!treeInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'tree.label', default: 'Tree'), params.id])
            redirect(action: "list")
            return
        } else {
            def charCsv = charService.convertCharJsonToCsv(treeInstance.characters , params.separator)
            //render charCsv
            response.setHeader "Content-disposition", "attachment; filename=${treeInstance.name}-charcters.csv"
            response.contentType = 'text/csv'
            response.outputStream << charCsv
            response.outputStream.flush()
        }
    }

    boolean checkPermissions(owner) {
        def currentUser = authService.getEmail()
        log.debug("owner = " + owner + " || userId = " + currentUser + " || ADMIN_ROLE = " + ADMIN_ROLE)
        if (currentUser != null && (currentUser.equalsIgnoreCase(owner) || authService.userInRole(ADMIN_ROLE))) {
            log.debug("returning true")
            return true;
        } else {
            flash.message = "You do not have permission to view this page"
            redirect(uri:"/")
        }
    }
}
